import { WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import * as iconv from "iconv-lite";
import * as path from 'path';


const endMarker = '__end__';

@WebSocketGateway({ cors: true })
export class TerminalGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
    @WebSocketServer() server: Server;

    private terminals: Map<string, ChildProcessWithoutNullStreams> = new Map();
    private inputBuffer: Map<string, string> = new Map<string, string>();
    private clientPaths: Map<string, string> = new Map<string, string>(); // Track client paths

    afterInit(server: Server) {
        console.log('WebSocket Gateway Initialized');
    }

    handleConnection(client: Socket) {
        console.log(`Client connected: ${client.id}`);

        // 启动本地终端（根据操作系统选择）
        const isWindows = process.platform === 'win32';
        const shell = isWindows ? 'powershell.exe' : '/bin/bash';
        const terminal = spawn(shell, [], { stdio: 'pipe' });

        // 设置初始路径（通常为当前工作目录）
        const initialDir = process.cwd();
        this.clientPaths.set(client.id, initialDir); // Store the initial path for this client


        // 保存终端实例
        this.terminals.set(client.id, terminal);

        // 启动连接时返回当前路径
        const outPath = () => {
            const currentPath = this.clientPaths.get(client.id) || initialDir;
            if (!isWindows) {
                client.emit('output', `\n$:${currentPath}>`);
            }
        }

        outPath();

        // 转发终端输出到客户端
        terminal.stdout.on('data', (data: Buffer) => {
            let utf8Data = iconv.decode(data, 'gbk');
            const buffer = this.inputBuffer.get(client.id) || "";
            if (buffer.length === 0) {
                utf8Data = '\r' + utf8Data;
            }
            console.log(utf8Data)
            client.emit("output", utf8Data);
            // 检查是否包含提示符（如 `$` 或 `>`）
            if (utf8Data.includes(endMarker)) {
                console.log('ondata outPath');
                outPath();
            }
        });

        terminal.stderr.on('data', (data) => {
            client.emit('output', data.toString());
        });

        terminal.on('close', () => {
            console.log(`Terminal closed for client: ${client.id}`);
        });

        // 处理用户输入
        client.on('message', (data) => {
            this.handleClientMessage(client, data);
        });
    }

    handleDisconnect(client: Socket) {
        console.log(`Client disconnected: ${client.id}`);

        // 清理终端实例
        const terminal = this.terminals.get(client.id);
        if (terminal) {
            terminal.kill();
            this.terminals.delete(client.id);
        }

        // 清理客户端路径
        this.clientPaths.delete(client.id);
    }

    @WebSocketServer()
    handleClientMessage(client: Socket, payload: string) {
        const terminal = this.terminals.get(client.id);

        if (!terminal) {
            console.warn(`Terminal not found for client: ${client.id}`);
            return;
        }

        const buffer = this.inputBuffer.get(client.id) || "";
        const currentDir = this.clientPaths.get(client.id) || process.cwd(); // 获取当前工作目录

        try {
            if (payload === '\r' || payload === '\n') {
                // 用户按下 Enter 键，发送缓冲区内容到终端
                for (let i = 0; i < buffer.length; i++) {
                    client.emit('output', '\b \b'); // 回显删除操作
                }

                // 执行命令时，将当前目录和命令一起返回
                const command = buffer.trim();
                if (command) {
                    client.emit('output', `${command}\n`); // 显示当前路径和命令
                    terminal.stdin.write(`${command} && echo ${endMarker}\n`, 'utf-8', (err) => {
                        if (err) {
                            console.error(`Error writing to terminal: ${err.message}`);
                        }
                    });
                    // 执行命令后，如果是路径切换命令（如 `cd`），则更新终端路径
                    if (command.startsWith('cd ')) {
                        const newPath = path.resolve(currentDir, command.slice(3).trim());
                        try {
                            process.chdir(newPath); // 修改 Node.js 的当前工作路径
                            this.clientPaths.set(client.id, newPath); // 更新客户端的路径
                        } catch (err) {
                            client.emit('output', `bash: cd: ${newPath}: No such file or directory\n`);
                        }
                    }
                }

                this.inputBuffer.set(client.id, ""); // 清空缓冲区
            } else if (payload === '\b' || payload === String.fromCharCode(127)) {
                // 处理 Backspace 键
                if (buffer.length > 0) {
                    const updatedBuffer = buffer.slice(0, -1); // 删除缓冲区最后一个字符
                    this.inputBuffer.set(client.id, updatedBuffer);
                    client.emit('output', '\b \b'); // 回显删除操作
                }
            } else if (payload === 'clear') {
                client.emit('output', '\x1B[2J\x1B[0f'); // 清空终端
            }else if (payload === String.fromCharCode(3)) { // Ctrl+C 的字符是 ASCII 3
                // terminal.kill('SIGINT'); // 发送 SIGINT 信号终止命令
                terminal.stdin.write('\x03'); // 发送 Ctrl+C 信号
                client.emit('output', '\n[Process interrupted]\n');
            }else {
                // 收集用户输入到缓冲区
                const updatedBuffer = buffer + payload;
                this.inputBuffer.set(client.id, updatedBuffer);
                client.emit('output', payload); // 回显输入到前端
            }

           

        } catch (err) {
            console.error(`Error processing client input: ${err.message}`);
        }
    }
}
